package iosu.java.games.razer;

import iosu.java.games.razer.api.IRazerProtocol;
import iosu.java.games.razer.api.RazerProtocol;
import iosu.java.games.razer.api.RazerProtocolMessage;
import iosu.java.games.razer.characters.Car;
import iosu.java.games.razer.server.ServerCar;

import java.applet.Applet;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.security.InvalidParameterException;
import java.text.DecimalFormat;
import java.util.UUID;

import javax.imageio.ImageIO;
import javax.management.InvalidApplicationException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** 
 * esta clase implementa el cliente de juego para Razer
 * @author iosu
 * 
 */
public class RazerClient extends Applet implements Runnable, KeyListener {

	private static final long serialVersionUID = -6144620762322911819L;


	// aqui van toooodos los sprites
	// luego los cargo en el init
	private Image image, character, enemy1img, enemy2img, enemy3img;
	private URL base;
	private Graphics second;
	// tengo 4 coches
	private Car car;
	private Car enemy1;
	private Car enemy2;
	private Car enemy3;
	// este es el que manejo
	private Car jugador;
	private InetAddress serverIP;
	private int serverPORT;
	protected String _serverIP = "127.0.0.1";
	protected int _serverPORT = 9911;
	private Socket sock;
	private UUID idcliente = UUID.randomUUID();
	protected ClientStatus status = ClientStatus.IDLE;
	private int serverid;
	private int carnum=0;
	private Font font;
//	private DecimalFormat df;
	protected short[][] tracktemplate ;
	protected BufferedImage track, trackart, splash;
	protected RazerProtocol razerprotocol;

//	private String idjuego;
	private Track track1;
//	private Track currentTrack;
	
	private static Logger logger;
	
	private static int countdownindex=0;
	private int lapnumber=10;
//	private int currentlap=1;;
	
	private int[] tmpresults;
	
	static{
		logger= LoggerFactory.getLogger(RazerClient.class);
	}

	@Override
	public void init() {
//		this.df = new DecimalFormat("#.##");
		this.status = ClientStatus.INITIALIZING;
		setSize(800, 480);
		setBackground(Color.BLACK);
		setFocusable(true);
		Frame frame = (Frame) this.getParent().getParent();
		frame.setTitle("RaZer!");
		base = getDocumentBase();
		loadGameResources();
	}
	
	/**
	 * este metodo se encarga de cargar todos los recursos del juego 
	 */
	protected void loadGameResources(){
		try {
			// para cargar los recursos del applet
			URL urlToFont = this.getClass().getResource("/data/BADABB.ttf");
			File file = new File(urlToFont.toURI());
			Font f = Font.createFont( Font.TRUETYPE_FONT, new FileInputStream(file) );
			font = f.deriveFont(36f);
			// instancio un objeto Track y que el se encarge de cargar las imagenes y generar la 
			// matriz con la info del circuito
			// despues al crear la partida dejaria elegir el circuito al jugador que la crea..
			// esto significa que o bien todos los jugadores deben tener el mismo set de circuitos
			// o bien tengo que empaquetar los dos pngs y enviarselos a cada jugador (desde el server?)
			
			// cargo la plantilla del circuito
			track = ImageIO.read(new File(this.getClass().getResource("/data/track01_1.png").toURI()));
			// y cargo el arte del dibujo
			trackart = ImageIO.read(new File(this.getClass().getResource("/data/trackart01.png").toURI()));
			// de momento currenttrack sera track1 hasta que implemente la seleccion de circuito
			track1 = new Track(track, trackart);
			// cargo la splash
			splash = ImageIO.read(new File(this.getClass().getResource("/data/splash.png").toURI()));
		} catch (Exception e) {
			logger.error("loadGameResources: error cargando resources ",e);
			System.err.println(e.toString());
			e.printStackTrace();
		}
		
		// supongo que aqui cargaria todos los sprites
		character = getImage(base, "data/car1_17x18.png");
		enemy1img = getImage(base, "data/enemy1_17x18.png");
		enemy2img = getImage(base, "data/enemy2_17x18.png");
		enemy3img = getImage(base, "data/enemy3_17x18.png");
		// luz de freno
		// estela de aceleracion
		// etc..
		// character = getImage(base, "data/character.png");
		// character2 = getImage(base, "data/character2.png");
		// character3 = getImage(base, "data/character3.png");
		// y para la animacion
		// anim = new Animation();
		// anim.addFrame(character, 1250);
		// anim.addFrame(character2, 50);
		// anim.addFrame(character3, 50);
		// anim.addFrame(character2, 50);
	}

	
	@Override
	public void start() {
		this.status = ClientStatus.STARTING;
		logger.debug("start: starting...");
		addKeyListener(this);
		car = new Car(character, 30, 260, "car");
		enemy1 = new Car(enemy1img, 60, 260, "enemy1");
		enemy2 = new Car(enemy2img, 90, 260,"enemy2");
		enemy3 = new Car(enemy3img, 110, 260,"enemy3");
		try {
			this.serverIP = InetAddress.getByName(_serverIP);
			this.serverPORT = _serverPORT;
		} catch (UnknownHostException e) {
			logger.error("start: error conectando a {} {}", _serverIP,e);
			System.exit(1);
		}
		Thread thread = new Thread(this);
		thread.start();
	}

	@Override
	public void stop() {
		super.stop();
	}

	@Override
	public void destroy() {
		this.status = ClientStatus.CLOSING;
		super.destroy();
		
	}

	@Override
	public void run() {
		logger.debug("run: arrancando networking");
		this.status=ClientStatus.STARTING;
		if (!initClientSockets()) {
			logger.error("run: no se puede arrancar la red...");
			System.exit(1);
		}
		startNetworking();
		logger.debug("run: entrando en bucle de juego");
		logger.warn("track1 hardcodeada");
		while (true) {
			manageNetworking();
			if(this.status==ClientStatus.PLAYING){
				// primero miro si estoy ralentizado
				if(track1==null) logger.error("ERROR! track1 es null");
				if(track1.getColorAt(jugador.getCenterX(), jugador.getCenterY())==Track.TRACK_GREEN){
					// si es TRACK_GREEN, estoy en la hierba
					// si el jugador no esta ralentizado, lo ralentizo
					if(!jugador.isSnared())	jugador.setSnared(true);
				}else {
					// estoy en la pista
					// si el jugador estaba ralentizado, lo normalizo
					if(jugador.isSnared()) jugador.setSnared(false);
				}
				// en estos updates uno de los coches puede llegar a meta.. !!
				car.update();
				enemy1.update();
				enemy2.update();
				enemy3.update();
				// reviso si el jugador ha terminado
				if(jugador.isFinished()){
					// terminado!!
					this.status=ClientStatus.CAR_FINISHED;
				}
			}
			// if(car.isAccelerating()){
			// currentSprite = acceleratingSprite
			// o para las animaciones: currentsprite = anim.getImage()
			// }.. y asi con todas las posibilidades
			//
			// y para las animaciones
			animate();
			repaint();
			// pauso el juegp 17  milisegundos, no necesito mas FPS
			try {
				Thread.sleep(17);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} // bucle del juego

	}

	
	/**
	 * este metodo registra el cliente en el servidor 
	 */
	private void startNetworking(){
		// aqui ya deberia tener el socket conectado al servidor
		// asi que envio el helo
		sendHelo();
		// y espero la respuesta desde el servidor 
		processServerResponse(RazerProtocol.decodeMessage(razerprotocol.receive().getMessageContent()));
	}

	/**
	 * este metodo implementa la logica de cambios de estado durante el bucle de juego 
	 */
	protected void manageNetworking(){
		// TODO
		// si todas las ramas del IF acaban leyendo la respuesta del servidor
		// deberia sacar fuera el processServerResponse
		// ADEMAS, deberia montarlo como un switch/case y no como un IF
		if(this.status==ClientStatus.WAITING_FOR_PLAYERS){
			// se ha pulsado 'C' solicito crear partida, yo sere el coche 1
			sendWaitingForPlayers();
			// y leo la respuesta
			processServerResponse(RazerProtocol.decodeMessage(razerprotocol.receive().getMessageContent()));
		}else if(this.status==ClientStatus.WAITING_FOR_PLAYERS_REQUESTED){
			// estoy esperando que los jugadores se unan a mi partida
			sendQueryForGame();
			// leo la respuesta del servidor
			processServerResponse(RazerProtocol.decodeMessage(razerprotocol.receive().getMessageContent()));
		}else if(this.status==ClientStatus.WAITING_FOR_GAME){
			// solicito unirme a una partida que ya este creada
			sendWaitingForGame();
			// y espero la respuesta desde el servidor 
			processServerResponse(RazerProtocol.decodeMessage(razerprotocol.receive().getMessageContent()));
		}else if(this.status==ClientStatus.WAITING_FOR_GAME_REQUESTED){
			// espero a que la partida empiece
			sendQueryForGame();
			// y espero la respueta del servido
			processServerResponse(RazerProtocol.decodeMessage(razerprotocol.receive().getMessageContent()));
		}else if(this.status==ClientStatus.PLAYING){
			// empieza la partida
			// seteo la pista
			
			jugador.setTrack(track1);
			// envio la info de mi coche
			sendCarStatus();
			// y leo la info de los otros 3
			processServerResponse(RazerProtocol.decodeMessage(razerprotocol.receive().getMessageContent()));
		}else if(this.status==ClientStatus.GAME_FINISHED){
			logger.info("GAME FINISHED");
		}else if(this.status==ClientStatus.CLOSING){
			logger.info("manageNetworking: cerrando cliente");
			sendClosingMessage();
			processServerResponse(RazerProtocol.decodeMessage(razerprotocol.receive().getMessageContent()));
		}else if(this.status==ClientStatus.CAR_FINISHED){
			logger.info("manageNetworking: el jugador "+this.serverid+" ha terminado la carrera");
			sendCarFinishedMessage();
//			System.out.println("enviado mensaje al servidor, esperando 1a respuesta");
			// leo la respuesta del servidor, si soy el ultimo me contestara con los resultados. Si no, me contestara un noop
			processServerResponse(RazerProtocol.decodeMessage(razerprotocol.receive().getMessageContent()));
		}else if(this.status==ClientStatus.WAITING_FOR_FINISH){
			// estoy esperando que el servidor me conteste con fin de partida
			logger.debug("manageNetworking: ha terminado la partida?");
//			System.out.println("pregunto si ha terminado mi carrera "+this.serverid);
			sendGameFinishedRequest();
//			System.out.println("esperando respuesta...");
			processServerResponse(RazerProtocol.decodeMessage(razerprotocol.receive().getMessageContent()));
			
		}else{
		
			logger.debug("manageNetworking: no se ha definido acciones para estado {}", this.status);
		}
		/*else if(this.status==ClientStatus.WAITING_FOR_GAMELIST){
			sendRequestGameList();
			try{
				String line = in.readLine();
				processResponse(line);
			}catch(IOException e){
				System.err.println(e.toString());
				this.status=ClientStatus.NETWORK_ERROR;
			}
		}*/
		
	}

	
	private ClientStatus processServerResponse(RazerProtocolMessage msg){
		switch(msg.getMessagetype()){
		case IRazerProtocol.HELLO_RESPONSE:
			if(this.status==ClientStatus.WAITING_FOR_HELLOOK){
				this.status=ClientStatus.REGISTERED;
			// 	el id que me ha asignado el servidor
				this.serverid = msg.getIdfromserver();
				logger.debug("processServerResponse: recibido HELLO_RESPONSE, cambiando status a REGISTERED, id de servidor recibido {}",msg.getIdfromserver());
				return ClientStatus.REGISTERED;
			}else{
				logger.debug("processServerResponse: recibido HELLO_RESPONSE pero el estado no es CONNECTED por lo que ignoro la respuesta");
				return this.status;
			}
		case IRazerProtocol.NOGAMETOJOIN_RESPONSE:
			// respuesta a unirse a una partida en el caso de que no haya ninguna partida en curso
			logger.debug("processServerResponse: recibido NOGAMETOJOIN, cambiando status a REGISTERED, id de servidor recibido {}", msg.getIdfromserver());
			this.status = ClientStatus.REGISTERED;
			return ClientStatus.REGISTERED;
		case IRazerProtocol.PLAYERADDED_RESPONSE:
			// respuesta de unirse a una partida cuando se ha unido el jugador a una partida
			logger.debug("processServerResponse: recibido PLAYERADDED");
			this.carnum = msg.getNumCar();
			if(this.carnum==1){
				jugador = car;
			}else if(this.carnum==2){
				// cambio car por el enemy1
				jugador = enemy1;
			}else if(this.carnum==3){
				// cambio car por el enemy2
				jugador = enemy2;
			}else if(this.carnum==4){
				// aqui no llega nunca pq el servidor no manda ADDEDEWPLAYER-4 sino GAMEREADY
				// cambio car por el enemy3
				jugador = enemy3;
			}
			return this.status;
		case IRazerProtocol.CREATEGAME_RESPONSE:
			logger.debug("processServerResponse: recibido CREATEGAME_RESPONSE");
			// si yo creo el juego, sere el coche 1
			this.carnum = 1;
			jugador=car;
			break;
		case IRazerProtocol.GAMEREADY_RESPONSE:
			// ya estamos los 4, se puede empezar el juego
			// solo le hago caso si soy el que ha creado la partida
			logger.debug("processServerResponse: recibido GAMEREADY_RESPONSE");
			 if(this.status==ClientStatus.WAITING_FOR_GAME_REQUESTED){
				// si mi carnum es 0 es que soy el 4 jugador
				if((this.carnum==0)||(jugador==null)){
					this.carnum=4;
					jugador = enemy3;
				}
			}
			 this.status=ClientStatus.GAME_READY;
			return ClientStatus.GAME_READY;
		case IRazerProtocol.NOOP_RESPONSE:
			logger.debug("processServerResponse: recibido NOOP_RESPONSE");
			// si recibo un noop, no hago nada.. normalmente me contestaran noop al perguntar
			// si una partida esta lista y no lo este aun
//			System.out.println("recibio noop");
			break;
		case IRazerProtocol.CARUPDATE_RESPONSE:
			logger.debug("processServerResponse: recibido CARUPDATE_RESPONSE");
			// en msg tengo los parametros nuevos de los coches
			if(this.status==ClientStatus.PLAYING){
				// en this.carnum tengo mi num de coche
				// coche1
				if(this.carnum!=1) updateCar(car, msg.getCar1update());
				// coche2
				if(this.carnum!=2) updateCar(enemy1,msg.getCar2update());
				// coche3
				if(this.carnum!=3) updateCar(enemy2,msg.getCar3update());
				// coche4
				if(this.carnum!=4) updateCar(enemy3, msg.getCar4update());
				if(jugador.getLapnum()==this.lapnumber){
					// juego terminado
					logger.debug("processServerResponse: juego terminado, ganador {}",jugador.getName());
					return ClientStatus.GAME_FINISHED;
				}else{
					return ClientStatus.PLAYING;
				}
			}else{
				logger.warn("processServerResponse: ... pero el estado no es PLAYING!");
			}
			break;
		case IRazerProtocol.CARFINISHED_RESPONSE:
			logger.info("processServerResponse: recibido CARFINISHED_RESPONSE");
//			System.out.println("recibido carfinished_response");
			// estoy esperando qeu el servidor me conteste con el resultado final de la carrera
			// en msg tengo la lista con los puestos 1,2,3,4
			this.tmpresults = msg.getResults();
			// camnbio el estado a showresults
			this.status=ClientStatus.SHOWRESULTS;
			return ClientStatus.SHOWRESULTS;
			
		}
		return null;
	}
	
		
private void updateCar(Car car, ServerCar data){
	
	car.setCenterX(data.getCenterx());
	car.setCenterY(data.getCentery());
	car.setOrientation(data.getRotation());
	car.setRotatingleft(data.isRotatingleft());
	car.setRotatingright(data.isRotatingright());
	car.setAccelerating(data.isAccelerating());
	car.setBraking(data.isBraking());
	car.setSnared(data.isSnared());
}


	
	/**
	 * este metodo envia el mensaje inicial solicitando el registro del cliente en el servidor
	 */
	private void sendHelo(){
		// me registro en el servidor
		logger.debug("sendHelo: enviando HELLO_REQUEST");
		try{
			long date = System.currentTimeMillis();
			razerprotocol.send(RazerProtocol.createHelloRequest(date, idcliente) );
			// cambio el estado
			this.status = ClientStatus.WAITING_FOR_HELLOOK;
			logger.debug("sendHelo: cambiando status a WAITING_FOR_HELLOOK");
		}catch(InvalidApplicationException|IOException e){
			logger.error("sendHelo: configuracion incorrecta al enviar HELLO_REQUEST o error IO",e);
		}
	}
	
	private void sendGameFinishedRequest(){
		logger.debug("sendGameFinishedRequest: enviando GAMEFINISHED_REQUEST");
		try{
			razerprotocol.send(RazerProtocol.createGameFinishedRequest(serverid));
		}catch(InvalidApplicationException|IOException e){
			logger.error("sendHelo: configuracion incorrecta al enviar HELLO_REQUEST o error IO",e);
		}
	}
	
	/**
	 * este metodo envia una peticion para unirse a alguna partida existente cualquiera
	 */
	private void sendWaitingForGame(){
		logger.debug("sendWaitingForGame: enviando JOINGAME_REQUEST");
		try{
			razerprotocol.send(RazerProtocol.createJoinRequest(serverid));
			logger.debug("sendWaitingForGame: cambiando status a WAITING_FOR_GAME_REQUESTED");
			this.status=ClientStatus.WAITING_FOR_GAME_REQUESTED;
		}catch(InvalidApplicationException|IOException e){
			logger.error("sendWaitingForGame: configuracion incorrecta al enviar JOINGAME_REQUEST o error IO",e);
		}
	}
	
	/**
	 * este metodo envia una peticion para crear una partida nueva
	 * y rellenarla con jugadores
	 */
	private void sendWaitingForPlayers(){
		logger.debug("sendWaitingForPlayers: enviando CREATEGAME_REQUEST");
		try{
			razerprotocol.send(RazerProtocol.createCreateGameRequest(serverid));
			logger.debug("sendWaitingForPlayers: cambiando status a WAITING_FOR_PLAYERS_REQUESTED");
			this.status=ClientStatus.WAITING_FOR_PLAYERS_REQUESTED;
		}catch(InvalidApplicationException|IOException e){
			logger.error("sendWaitingForPlayers: configuracion incorrecta al enviar CREATEGAME_REQUEST o error IO",e);
		}
	}
	
	/**
	 * este metodo envia una consulta sobre el estado de una partida
	 * si esta lista para empezar o no
	 */
	private void sendQueryForGame(){
		logger.debug("sendQueryForGame: enviando GAMEREADY_REQUEST");
		try{
			razerprotocol.send(RazerProtocol.createGameReadyRequest(serverid));
			// esta peticion no cambia el estado a menos que la respuesta desde el servidor
			// sea un GAMEREADY
		}catch(InvalidApplicationException|IOException e){
			logger.error("sendQueryForGame: configuracion incorrecta al enviar GAMEREADY_REQUEST o error IO", e);
		}
	}
	
	private void sendCarStatus(){
		logger.debug("sendCarStatus: enviando CARUPDATE_REQUEST");
		try{
			razerprotocol.send(RazerProtocol.createCarUpdateRequest(jugador, this.carnum, serverid));
		}catch(InvalidApplicationException|IOException e){
			logger.error("sendCarStatus: configuracion incorrecta al enviar CARUPDATE_REQUEST o error IO");
		}

	}
	
	private void sendClosingMessage(){
		logger.debug("sendClosingMessage: enviando CLOSING");
		try{
			razerprotocol.send(RazerProtocol.createClosingRequest(serverid));
		}catch(InvalidApplicationException|IOException e){
			logger.error("sendClosingMessage: configuracion incorrecta al enviar CLOSING_REQUEST o error IO");
		}	
		
	}
	
	private void sendCarFinishedMessage(){
		logger.debug("sendCarFinishedMessage: enviando CARFINISHED_REQUEST");
		try{
			razerprotocol.send(RazerProtocol.createCarFinishedRequest(serverid));
			this.status=ClientStatus.WAITING_FOR_FINISH;
		}catch(InvalidApplicationException|IOException e){
			logger.error("sendClosingMessage: configuracion incorrecta al enviar CARFINISHED_REQUEST o error IO");
		}
	}
	
	// en el metodo animate meto todas las animaciones que he cargado al inicio
	private void animate() {
		// anim.update(10);
	}

	@Override
	public void update(Graphics g) {
		if (image == null) {
			image = createImage(this.getWidth(), this.getHeight());
			second = image.getGraphics();
		}

		second.setColor(getBackground());
		second.fillRect(0, 0, getWidth(), getHeight());
		second.setColor(getForeground());
		paint(second);
		g.drawImage(image, 0, 0, this);
	}

	@Override
	public void paint(Graphics g) {

		Graphics2D g2d = (Graphics2D) g;
		g2d.setColor(getBackground());
		g2d.fillRect(0,0,getWidth(),getHeight());
		if(this.status!=ClientStatus.PLAYING){
			// solo pinto el fondo mientras no este jugando
			g2d.drawImage(splash, 0,0,this);
		}
		g2d.setColor(Color.WHITE);
		g2d.setFont(font);
		
		// y ahora los estados
		if(this.status==ClientStatus.WAITING_FOR_GAME_REQUESTED){
			g2d.drawString("uniendose a partida...",200,50);
		}else if(this.status==ClientStatus.WAITING_FOR_PLAYERS_REQUESTED){
			g2d.drawString("creando partida...",200,100);
		}else if(this.status==ClientStatus.REGISTERED){
			g2d.drawString("'C' - crear partida nueva", 200,50);
			g2d.drawString("'J' - unirse a partida", 200,100);
		}else if((this.status==ClientStatus.WAITING_FOR_GAME)
				||(this.status==ClientStatus.WAITING_FOR_GAME_REQUESTED)){
			g2d.setColor(Color.WHITE);
			g2d.drawString("'C' - crear partida nueva", 200,50);
			g2d.setColor(Color.YELLOW);
			g2d.drawString("'J' - unirse a partida", 200,100);
		}else if((this.status==ClientStatus.WAITING_FOR_PLAYERS)
				||(this.status==ClientStatus.WAITING_FOR_PLAYERS_REQUESTED)){
			g2d.setColor(Color.YELLOW);
			g2d.drawString("'C' - crear partida nueva", 200,50);
			g2d.setColor(Color.WHITE);
			g2d.drawString("'J' - unirse a partida", 200,100);
		}else if(this.status==ClientStatus.GAME_READY){ // 985748
			g2d.setColor(Color.WHITE);
			g2d.drawString("'Y' - EMPEZAR PARTIDA!", 200, 200);
		}else if(this.status==ClientStatus.ONYOURMARKS){
			// cuenta atras
				g2d.setColor(Color.MAGENTA);
				if(countdownindex==0){
					g2d.drawString("TRES!", 200, 200);
					countdownindex=1;
				}else if(countdownindex==1){
					g2d.drawString("DOS!", 300, 200);
					countdownindex=2;
					try { Thread.sleep(1000); }
					catch (InterruptedException e) { }
				}else if(countdownindex==2){
					g2d.drawString("UNO!", 400, 200);
					countdownindex=3;
					try { Thread.sleep(1000); }
					catch (InterruptedException e) { }
					
				}else if(countdownindex==3){
					this.status=ClientStatus.PLAYING;
					countdownindex=0;
					try { Thread.sleep(1000); }
					catch (InterruptedException e) { }
				}
				

		}else if((this.status==ClientStatus.PLAYING)){
			g2d.drawImage(track1.getTrackart(), 0, 0, this);
			// y pinto la vuelta
			g2d.drawString(String.valueOf(jugador.getLapnum())+"/"+String.valueOf(this.lapnumber), 700, 50);
			g2d.drawPolygon(jugador.getTrack().getNextMilestone().getForma());
			car.paint(g2d, this);
			enemy1.paint(g2d, this);
			enemy2.paint(g2d, this);
			enemy3.paint(g2d, this);
		}else if(this.status==ClientStatus.GAME_FINISHED){
			g2d.drawImage(track1.getTrackart(), 0, 0, this);
			// y pinto la vuelta
			g2d.drawString(String.valueOf(jugador.getLapnum())+"/"+String.valueOf(this.lapnumber), 700, 50);
			g2d.drawString("FINISH!", 300, 200);
		}else if(this.status==ClientStatus.SHOWRESULTS){
			g2d.drawString("1 - "+this.tmpresults[0], 200, 100);
			g2d.drawString("2 - "+this.tmpresults[1], 200, 150);
			g2d.drawString("3 - "+this.tmpresults[2], 200, 200);
			g2d.drawString("4 - "+this.tmpresults[3], 200, 250);
			g2d.drawString("'K' - continuar", 200, 400);
		}else if(this.status==ClientStatus.WAITING_FOR_FINISH){
			g2d.drawString("META!!", 300, 100);
			g2d.drawString("esperando al resto de corredores... ",150,150);
		}
		g2d.setColor(Color.RED);
		g2d.drawString(this.status.toString(),10, 450);
		String carnum = String.valueOf(this.carnum);
		String playername = jugador!=null?jugador.getName():"null";
		g2d.drawString("soy el coche "+carnum+" jugador="+playername, 10, 400);
		
		
	}

	@Override
	public void keyPressed(KeyEvent e) {
		// al pulsar las teclas cambio el estado
		// asi puedo escuchar varias teclas a la vez
		switch (e.getKeyCode()) {
		// acelerar
		case KeyEvent.VK_W:
			// solamente si la partida esta en curso
			if(this.status==ClientStatus.PLAYING) jugador.setAccelerating(true);
			break;
		// frenar
		case KeyEvent.VK_S:
			// solamente si la partida esta en curso
			if(this.status==ClientStatus.PLAYING) jugador.setBraking(true);
			break;
		// giro izq
		case KeyEvent.VK_A:
			// solamente si la partida esta en curso
			if(this.status==ClientStatus.PLAYING) jugador.setRotatingleft(true);
			break;
		// giro dcha
		case KeyEvent.VK_D:
			// solamente si la partida esta en curso
			if(this.status==ClientStatus.PLAYING) jugador.setRotatingright(true);
			
		
		}
	}

	@Override
	public void keyReleased(KeyEvent e) {
		// al liberar las teclas paro los movimientos
		switch (e.getKeyCode()) {
		// arriba
		case KeyEvent.VK_W:
			// solamente si la partida esta en curso
			if(this.status==ClientStatus.PLAYING) jugador.setAccelerating(false);
			break;
		// abajo
		case KeyEvent.VK_S:
			// solamente si la partida esta en curso
			if(this.status==ClientStatus.PLAYING) jugador.setBraking(false);
			break;
		// izq
		case KeyEvent.VK_A:
			// solamente si la partida esta en curso
			if(this.status==ClientStatus.PLAYING) jugador.setRotatingleft(false);
			break;
		// dcha
		case KeyEvent.VK_D:
			// solamente si la partida esta en curso
			if(this.status==ClientStatus.PLAYING) jugador.setRotatingright(false);
			// y luego varios disparos para usar las mejoras
			break;
		case KeyEvent.VK_J:
			if(this.status==ClientStatus.REGISTERED){
				logger.debug("keyreleased: 'J' cambiando estado a WAITING_FOR_GAME");
				this.status = ClientStatus.WAITING_FOR_GAME;
			}
			break;
		case KeyEvent.VK_C:
			if(this.status==ClientStatus.REGISTERED){
				logger.debug("keyreleased: 'C' cambiando estado a WAITING_FOR_PLAYERS");
				this.status = ClientStatus.WAITING_FOR_PLAYERS;
			}
			break;
		case KeyEvent.VK_K:
			if(this.status==ClientStatus.SHOWRESULTS){
				this.status = ClientStatus.REGISTERED;
				this.jugador.setLapnum(1);
//				this.currentlap=1;
				
			}
			break;
		case KeyEvent.VK_Y:
			if(this.status==ClientStatus.GAME_READY) this.status = ClientStatus.ONYOURMARKS;
			break;
		}
	}

	@Override
	public void keyTyped(KeyEvent e) {
	}

	protected boolean initClientSockets() {
		// inicializo el socket cliente
		try {
			sock = new Socket(this.serverIP, this.serverPORT);
			razerprotocol = new RazerProtocol(sock);
			logger.debug("socket y razerprotocol creados");
			// si uso el razerprotocol no me hacen falta estos buffers..
			//in = new BufferedReader( new InputStreamReader( sock.getInputStream( ) ));
			//out = new PrintWriter( sock.getOutputStream( ), true );
			if(this.status==ClientStatus.STARTING) this.status = ClientStatus.CONNECTED;
			}catch(IOException e){ 
				logger.error("error en la inicializacion de sockets []",e);
				this.status = ClientStatus.NETWORK_ERROR;
				return false;
			}
		return true;
	}

}
